
import socket
import threading 
import logging
from socketserver import ThreadingTCPServer, BaseRequestHandler


CRITICAL = logging.CRITICAL
FATAL = logging.FATAL
ERROR = logging.ERROR
WARNING = logging.WARNING
WARN = logging.WARN
INFO = logging.INFO
DEBUG = logging.DEBUG
NOTSET = logging.NOTSET


defaultFormatter = logging.Formatter('%(asctime)s [%(name)s] [%(levelname)s] %(funcName)s "%(pathname)s", line %(lineno)d %(message)s')
#defaultFormatter = logging.Formatter('{asctime} [{name}:{levelname}]\t[{funcName}] "{pathname}", line {lineno}\t{message}')


def SocketFunc(*args, **kwargs):
    pass

class SocketCommend:
    
    commends:dict = {}
    '''
    dict{cmd:str, [func, notes:str]}
    '''

    @classmethod
    def register(cls, cmd:str, topic:str, func:SocketFunc):
        if cmd not in cls.commends:
            print("register cmd: %s"%cmd)
            cls.commends[cmd] = [func, topic]

    @classmethod
    def exec(cls, cmd, *args, **kwargs):
        func, topic = cls.commends.get(cmd, None)
        func(*args, **kwargs)


def register(cmd, topic):

    def decorator(fn):
        SocketCommend.register(cmd, topic, fn)

        def inner(*args, **kwargs):
            ret = fn(*args, **kwargs)
            return ret
        return inner

    return decorator

def commends():
    return SocketCommend.commends

class SocketClientRequest(BaseRequestHandler):

    header = "service->"

    def setup(self) -> None:
        print("setup")

        self.request.settimeout(5)

    def handle(self):
        data:bytes=b''
        while True:

            try:
                data = self.request.recv(1024)
            except socket.timeout:
                continue
            except OSError as e:
                print(e)
                break

            # 客户端主动断开，则接收到数据长度为0，结束该监听线程
            if len(data) == 0:
                print("len == 0")
                break

            try:
                message = data.decode().strip()
            except UnicodeDecodeError:
                print("date is decode failed, drop it")
            else:
                # TODO 解析 telnet 命令
                if "bye" in message:
                    print("bye")
                    break
                
                if message == "":
                    response = self.header
                    self.request.sendall(response.encode())
                    continue

                # TODO 执行命令
                va_list = message.split(" ")
                SocketCommend.exec(*va_list)
                response = "commend success\n" + self.header
                self.request.sendall(response.encode())

            

class SocketServerHandler(logging.Handler, ThreadingTCPServer):
    """
    """

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = True

    def __init__(self, host:str, port:int ):
        """Constructor.  May be extended, do not override."""

        logging.Handler.__init__(self)
        ThreadingTCPServer.__init__(self, (host, port), SocketClientRequest)

        self.t = threading.Thread(target= self.serve_forever)
        self.t.start()

        self._client_fds:list[socket.socket] = []

    def get_request(self):
        request, client_address = self.socket.accept()
        print("%s connect success"%(request))

        if request not in self._client_fds:
            self._client_fds.append(request)
        return (request, client_address)

    def close_request(self, request):
        """Called to clean up an individual request."""
        request.close()
        print("%s disconnect"%(request))
        if request in self._client_fds:
            self._client_fds.remove(request)

    def send(self, s):
        """
        Send a pickled string to the socket.

        This function allows for partial sends which can happen when the
        network is busy.
        """

        # sock can be None either because we haven't reached the retry
        #time yet, or because we have reached the retry time and retried,
        #but are still unable to connect.
        for sock in self._client_fds:
            # if sock is None:
            #     continue
            try:
                sock.sendall(s)
            except OSError as e: #pragma: no cover
                sock.close()
                print(e)
                #sock = None  # so we can call createSocket next time

    def emit(self, record):
        """
        Emit a record.

        Pickles the record and writes it to the socket in binary format.
        If there is an error with the socket, silently drop the packet.
        If there was a problem with the socket, re-establishes the
        socket.
        """
        try:
            msg = self.format(record) + "\n"
            self.send(msg.encode('utf-8'))
        except Exception:
            self.handleError(record)


    def close(self):
        """
        Closes the socket.
        """
        self.acquire()
        try:
            for sock in self._client_fds:
                sock.close()
        finally:
            logging.Handler.close(self)
            # logging.handler lock 释放
            self.release()

            # 由于 server_forever() 是跑在线程内的，所以当需要关闭时，shutdown 来停止线程
            self.shutdown()

            # tcp server fd 释放
            self.server_close()

